package org.apache.solr.search.function.distance;

import com.spatial4j.core.io.GeohashUtils;
import java.io.IOException;
import java.util.Map;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;


/**
 * Takes in a latitude and longitude ValueSource and produces a GeoHash.
 * <p/>
 * Ex: geohash(lat, lon)
 *
 * <p/>
 * Note, there is no reciprocal function for this.
 **/
public class GeohashFunction extends ValueSource {

    protected ValueSource lat, lon;

    public GeohashFunction(ValueSource lat, ValueSource lon) {
        this.lat = lat;
        this.lon = lon;
    }

    protected String name() {
        return "geohash";
    }

    @Override
    public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {

        final FunctionValues latDV = lat.getValues(context, readerContext);
        final FunctionValues lonDV = lon.getValues(context, readerContext);


        return new FunctionValues() {
            @Override
            public String strVal(int doc) {
                return GeohashUtils.encodeLatLon(latDV.doubleVal(doc), lonDV.doubleVal(doc));
            }

            @Override
            public String toString(int doc) {
                StringBuilder sb = new StringBuilder();
                sb.append(name()).append('(');
                sb.append(latDV.toString(doc)).append(',').append(lonDV.toString(doc));
                sb.append(')');
                return sb.toString();
            }
        };
    }

    @Override
    public boolean equals(Object o) {

        if (this == o) {
            return true;
        }
        if (!(o instanceof GeohashFunction)) {
            return false;
        }

        GeohashFunction that = (GeohashFunction) o;

        if (!lat.equals(that.lat)) {
            return false;
        }
        if (!lon.equals(that.lon)) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {

        int result = lat.hashCode();
        result = 29 * result - lon.hashCode();
        return result;
    }

    @Override
    public String description() {

        StringBuilder sb = new StringBuilder();
        sb.append(name()).append('(');
        sb.append(lat).append(',').append(lon);
        sb.append(')');
        return sb.toString();
    }
}
